#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.basic.module.menu.service.impl;

import ${package}.basic.module.element.mapper.ElementMapper;
import ${package}.basic.module.menu.mapper.MenuMapper;
import ${package}.basic.module.menu.service.MenuService;
import ${package}.basic.module.operate.mapper.OperateMapper;
import ${package}.basic.module.permission.mapper.PermissionMapper;
import ${package}.common.bean.po.Element;
import ${package}.common.bean.po.ElementPermission;
import ${package}.common.bean.po.Menu;
import ${package}.common.bean.po.MenuPermission;
import ${package}.common.bean.po.MenuView;
import ${package}.common.bean.po.Operate;
import ${package}.common.bean.po.OperatePermission;
import ${package}.common.bean.po.Role;
import ${package}.common.bean.po.RolePermission;
import ${package}.common.bean.po.User;
import ${package}.common.bean.vo.Constraint;
import ${package}.common.bean.vo.QueryBean;
import ${package}.common.bean.vo.SubPermission;
import ${package}.common.bean.vo.ViewTree;
import ${package}.common.emum.MenuStatusEnum;
import ${package}.common.emum.PermissionTypeEnum;
import ${package}.common.emum.ReturnCodeEnum;
import ${package}.common.exception.ValidException;
import ${package}.common.utils.TreeUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Description: 无法补充
 *
 * @author wupanhua
 * @date 2019/8/6 15:28
 *
 * <pre>
 *              ${copyright}
 *      Copyright (c) 2019. All Rights Reserved.
 * </pre>
 */
@Service
@AllArgsConstructor
public class MenuServiceImpl implements MenuService {

    /**
     * 菜单Mapper
     */
    MenuMapper menuMapper;
    /**
     * 元素Mapper
     */
    ElementMapper elementMapper;
    /**
     * 操作Mapper
     */
    OperateMapper operateMapper;
    /**
     * 权限Mapper
     */
    PermissionMapper permissionMapper;

    /**
     * Description:
     * <分页获得菜单>
     * @author wupanhua
     * @date 17:45 2019/8/6
     * @param queryBean 1
     * @return com.github.pagehelper.PageInfo<Menu>
     **/
    @Override
    public PageInfo<Menu> queryMeunListByLimit(QueryBean queryBean){

        // 筛选状态
        Constraint constraint = Constraint.build()
                .putField("sub.status")
                .putOp(Constraint.LOPERATE.IN.getOperation())
                .putValus(Boolean.TRUE, MenuStatusEnum.ENABLED.getCode(), MenuStatusEnum.DISABLE.getCode())
                .putValueType(Constraint.DATATYPE.STRING.getType());
        queryBean.and(constraint);

        Page<Menu> page = PageMethod.startPage(queryBean.getCurrent(), queryBean.getPageSize());
        menuMapper.queryMenuListByLimit(queryBean);

        return new PageInfo<>(page);
    }

    /**
     * Description:
     * <新增菜单>
     * @author wupanhua
     * @date 17:45 2019/8/6
     * @param menu 1
     **/
    @Override
    public void addMenu(Menu menu){
        menu.setCreatedate(new Date());
        menuMapper.insertMeun(menu);
    }

    /**
     * Description:
     * <获取当前用户下的权限列表>
     * @author wupanhua
     * @date 17:45 2019/8/6
     * @param user 1
     * @return java.util.List<Menu>
     **/
    @Override
    public List<Menu> queryMenuByCurrentUser(User user) {

        // 获取当前用户下的所有菜单

        // 获取当前角色下的所有菜单

        return Lists.newArrayList();
    }

    /**
     * Description:
     * <获取所有菜单(不包括被禁用的,被删除的)>
     * @author wupanhua
     * @date 17:48 2019/8/6
     * @return java.util.List<Menu>
     **/
    @Override
    public List<Menu> queryAllMenu(){
        return menuMapper.queryAllMenu();
    }

    /**
     * Description:
     * <获取树形插件(不包括被禁用的,被删除的)>
     * @author wupanhua
     * @date 17:48 2019/8/6
     * @return java.util.List<ViewTree>
     **/
    @Override
    public List<ViewTree> treeMenu(){

        // 获取所有菜单
        List<Menu> menus = menuMapper.queryAllMenu();
        // 转换成树形实体类
        List<ViewTree> trees = new ArrayList<>();
        for (Menu menu : menus) {
            trees.add(new ViewTree(menu.getId(), menu.getName(), Boolean.TRUE, menu.getUrl(), menu.getParentid(), null));
        }
        return TreeUtil.fillingTree(trees);
    }

    /**
     * Description:
     * <根据菜单主键获取下级权限>
     * @author wupanhua
     * @date 17:49 2019/8/6
     * @param menuKey 1
     * @param roleKey 2
     * @return java.util.List<SubPermission>
     **/
    @Override
    public List<SubPermission> querySubPermission(Long menuKey, Long roleKey){

        // 获取到当前的菜单
        Menu menu = menuMapper.queryMenuByMenuKey(menuKey);

        // 获取当前菜单的所有子菜单
        List<Menu> menus = menuMapper.queryMenuByParentId(menu.getId());
        // 获取页面元素权限
        List<Element> elements = elementMapper.queryElementByMenuKey(menuKey);
        // 获取操作权限
        List<Operate> operates = operateMapper.queryOperateByMenuKey(menuKey);

        // 获取用户已有的权限
        Set<Long> menuPermIdSet = Sets.newHashSet();
        Set<Long> elePermIdSet = Sets.newHashSet();
        Set<Long> opsPermIdSet = Sets.newHashSet();
        List<RolePermission> rolePermissions = permissionMapper.queryPermissionByRoleKey(roleKey);
        Map<String, List<RolePermission>> rolePermissionsMap = rolePermissions.stream().collect(Collectors.groupingBy(RolePermission::getPermissionTypeCode));
        List<RolePermission> menuPerm = rolePermissionsMap.get(PermissionTypeEnum.MENU.getCode());
        List<RolePermission> elePerm = rolePermissionsMap.get(PermissionTypeEnum.ELEMENT.getCode());
        List<RolePermission> opsPerm = rolePermissionsMap.get(PermissionTypeEnum.OPERATE.getCode());
        if (CollectionUtils.isNotEmpty(menuPerm)) {
            List<MenuPermission> hasMenuPermissions = permissionMapper.queryMenuPermissionByRolePermissionKey(menuPerm.get(0).getId());
            menuPermIdSet = hasMenuPermissions.stream().map(MenuPermission::getMenuId).collect(Collectors.toSet());
        }
        if (CollectionUtils.isNotEmpty(elePerm)) {
            List<ElementPermission> hasElementPermissions = permissionMapper.queryElementPermissionByRolePermissionKey(elePerm.get(0).getId());
            elePermIdSet = hasElementPermissions.stream().map(ElementPermission::getElementId).collect(Collectors.toSet());
        }
        if (CollectionUtils.isNotEmpty(opsPerm)) {
            List<OperatePermission> hasOperatePermissions = permissionMapper.queryOperatePermissionByRolePermissionKey(opsPerm.get(0).getId());
            opsPermIdSet = hasOperatePermissions.stream().map(OperatePermission::getOperateId).collect(Collectors.toSet());
        }
        SubPermission sp = new SubPermission();
        // 组装数据
        List<SubPermission> subPermissions = new ArrayList<>();
        for (Menu m : menus) {
            sp.setKey(m.getId());
            sp.setPermissionName(m.getName());
            sp.setPermissionType(PermissionTypeEnum.MENU.getName());
            sp.setPermissionCode(PermissionTypeEnum.MENU.getCode());
            sp.setParentMenu(menu.getName());
            sp.setHasPermission(Boolean.FALSE);
            if(menuPermIdSet.contains(m.getId())) {
                sp.setHasPermission(Boolean.TRUE);
            }
            subPermissions.add(sp);
        }
        for (Element e : elements) {
            sp.setKey(e.getId());
            sp.setPermissionName(e.getName());
            sp.setPermissionType(PermissionTypeEnum.ELEMENT.getName());
            sp.setPermissionCode(PermissionTypeEnum.ELEMENT.getCode());
            sp.setParentMenu(menu.getName());
            sp.setHasPermission(Boolean.FALSE);
            if(elePermIdSet.contains(e.getId())) {
                sp.setHasPermission(Boolean.TRUE);
            }
            subPermissions.add(sp);
        }
        for (Operate o : operates) {
            sp.setKey(o.getId());
            sp.setPermissionName(o.getName());
            sp.setPermissionType(PermissionTypeEnum.OPERATE.getName());
            sp.setPermissionCode(PermissionTypeEnum.OPERATE.getCode());
            sp.setParentMenu(menu.getName());
            sp.setHasPermission(Boolean.FALSE);
            if(opsPermIdSet.contains(o.getId())) {
                sp.setHasPermission(Boolean.TRUE);
            }
            subPermissions.add(sp);
        }
        return subPermissions;
    }

    /**
     * Description:
     * <获得当前权限下的所有菜单>
     * @author wupanhua
     * @date 17:50 2019/8/6
     * @param roles 1
     * @return java.util.Map<java.lang.String,java.lang.Object>
     **/
    @Override
    public HashMap<String, Object> queryCurrentPermissionMenu(List<Role> roles){

        // 获取当前权限下的
        List<Menu> menus = menuMapper.queryPermissionMenuByRoleKey(roles);

        // 转换成树形菜单
        List<Menu> tree = new ArrayList<>();
        for (Menu menu: menus) {
            if (menu.getParentid() == 0) {
                tree.add(menu);
            }
        }

        for (Menu menu: tree) {
            menu.setMenus(queryChildMenu(menu.getId(), menus));
        }

        HashMap<String, Object> map = Maps.newHashMap();
        List<MenuView> menuViewList;
        menuViewList = new ArrayList<>();
        //存放视图类的path和component字段
        String path;
        for(Menu menu:menus){
            if(menu.getParentid() == 0){
                path = menu.getUrl().substring(menu.getUrl().lastIndexOf('/') + 1);
                MenuView menuView = MenuView.builder().id(menu.getId()).path(path).name(menu.getName()).component(path)
                        .componentPath(menu.getUrl()).redirect(menu.getUrl()).build();
                menuView.setChildren(getChildMenuList(menu.getId(),menus));
                menuViewList.add(menuView);
            }
        }
        setMenuViewIdNull(menuViewList);
        map.put("tree", tree);
        map.put("list", menuViewList);
        return map;
    }

    /**
     * Description:
     * <无法补充>
     * @author wupanhua
     * @date 17:50 2019/8/6
     * @param id 1
     * @param root 2
     * @return java.util.List<Menu>
     **/
    private List<Menu> queryChildMenu(Long id, List<Menu> root) {
        // 子菜单
        List<Menu> childList = new ArrayList<>();
        for (Menu menu : root) {
            // 遍历所有节点，将父菜单id与传过来的id比较
            if (menu.getParentid() != null && menu.getParentid().longValue() == id.longValue()) {
                childList.add(menu);
            }
        }
        // 把子菜单的子菜单再循环一遍
        for (Menu menu : childList) {
            // 没有url子菜单还有子菜单
            if (!StringUtils.isEmpty(menu.getUrl())) {
                // 递归
                menu.setMenus(queryChildMenu(menu.getId(), root));
            }
        }
        // 递归退出条件
        return childList;
    }

    /**
     * Description:
     * <根据id删除菜单>
     * @author wupanhua
     * @date 17:51 2019/8/6
     * @param id 1
     **/
    @Override
    public void deleteMenuById(Long id){
        // 查询该菜单是否有子级目录
        int count = menuMapper.queryMenuCountByParentid(id);
        if(count != 0){
            throw new ValidException(ReturnCodeEnum.CURRENT_ELEMENT_WAS_OCCUPIED);
        }
        // 查询该菜单是否有角色占用
        int integer = permissionMapper.queryMenuPermissionCountById(id);
        if(integer != 0){
            throw new ValidException(ReturnCodeEnum.CURRENT_ELEMENT_WAS_OCCUPIED);
        }
        //将菜单状态置为删除
        menuMapper.updateMenuStatusDeleteById(id);
    }

    /**
     * Description:
     * <禁用/启用菜单>
     * @author wupanhua
     * @date 17:51 2019/8/6
     * @param menu 1
     **/
    @Override
    public void disableOrEnableMenu(Menu menu){
        // 查询该菜单是否有角色占用
        int integer = permissionMapper.queryMenuPermissionCountById(menu.getId());
        // 如果被占用不能被禁用
        if(integer != 0){
            throw new ValidException(ReturnCodeEnum.CURRENT_ELEMENT_WAS_OCCUPIED);
        }
        //判断传来的状态更新为相反状态
        if(menu.getStatus().intValue() == MenuStatusEnum.ENABLED.getCode().intValue()){
            menu.setStatus(MenuStatusEnum.DISABLE.getCode());
        }else {
            menu.setStatus(MenuStatusEnum.ENABLED.getCode());
        }
        // 禁用菜单
        menuMapper.updateMenuStatus(menu);
    }

    /**
     * Description:
     * <根据id查询菜单信息>
     * @author pig
     * @date 17:52 2019/8/6
     * @param id 1
     * @return Menu
     **/
    @Override
    public Menu queryMenuById(Long id){
        return menuMapper.queryMenuById(id);
    }

    /**
     * Description:
     * <根据id更新菜单信息>
     * @author pig
     * @date 17:53 2019/8/6
     * @param menu 1
     **/
    @Override
    public void updateMenuById(Menu menu){
        menuMapper.updateMenuById(menu);
    }

    /**
     * Description:
     * <查询所有的子菜单>
     * @author pig
     * @date 17:54 2019/8/6
     * @return java.util.List<Menu>
     **/
    @Override
    public List<Menu> queryAllChildren(){
        // 获取所有菜单
        return menuMapper.queryAllChildren();
    }

    /**
     * Description:
     * <批量删除菜单>
     * @author pig
     * @date 17:54 2019/8/6
     * @param keys 1
     * @return java.util.List<Menu>
     **/
    @Override
    public List<Menu> removeMenusBykeys(List<Long> keys) {

        List<Menu> result = Lists.newArrayList();
        // 区分被角色占用的菜单
        List<Long> occupied = new ArrayList<>();
        List<Long> free = new ArrayList<>();

        // 根据主键批量查询子菜单判断是否有子菜单
        List<Menu> menus = menuMapper.findSubMenuByKeys(keys);
        Set<Long> collect = menus.stream().map(Menu::getParentid).collect(Collectors.toSet());
        if(CollectionUtils.isNotEmpty(collect)) {
            keys.forEach(key -> {
                if(collect.contains(key)){
                    occupied.add(key);
                } else {
                    free.add(key);
                }
            });
        }

        List<Long> frees = new ArrayList<>();
        // 判断是否有角色占用该菜单
        if (!free.isEmpty()){
            List<MenuPermission> permissions = permissionMapper.findMenuPermissionsByMenuKeys(free);
            if(CollectionUtils.isNotEmpty(permissions)) {
                Set<Long> menuids = permissions.stream().map(MenuPermission::getMenuId).collect(Collectors.toSet());
                free.forEach(id -> {
                    if(menuids.contains(id)) {
                        occupied.add(id);
                    } else {
                        frees.add(id);
                    }
                });
            }
        }
        //将菜单状态置为删除
        if(!frees.isEmpty()){
            menuMapper.updateMenuStatusDeleteByKeys(frees);
        }

        if (!occupied.isEmpty()){
            // 根据id批量查询菜单信息
            result = menuMapper.findMenusByKeys(occupied);
        }
        return result;
    }

    /**
     * Description:
     * <查询name是否重复>
     * @author pig
     * @date 17:55 2019/8/6
     * @param name 1
     * @param id 2
     * @return java.lang.Boolean
     **/
    @Override
    public Boolean menuVerifyName(String name, Long id){

        if(id == null){
            return CollectionUtils.isEmpty(menuMapper.findMenuByNaem(name));
        } else {
            return CollectionUtils.isEmpty(menuMapper.findMenuByNaemOrNotInId(new Menu(id, name)));
        }
    }

    /**
     * Description:
     * <根据传入的菜单id和菜单列表，当前菜单id下的子菜单>
     * @author songcx
     * @date 9:31 2019/8/27
     * @param id 1
     * @param menuList 2
     * @return java.util.List<MenuView>
     **/
    private List<MenuView> getChildMenuList(Long id,List<Menu> menuList){
        //子菜单列表
        List<MenuView> menuViewList;
        menuViewList = new ArrayList<>();
        //存放视图类的path和component字段
        String path;
        for(Menu menu :menuList){
            if(id.equals(menu.getParentid())){
                path = menu.getUrl().substring(menu.getUrl().lastIndexOf('/') + 1);
                MenuView menuView = MenuView.builder().id(menu.getId()).path(path).name(menu.getName()).component(path)
                        .componentPath(menu.getUrl()).build();
                menuViewList.add(menuView);
            }
        }
        //遍历子菜单列表，递归处理子菜单的子菜单
        for(MenuView menuView:menuViewList){
            menuView.setChildren(getChildMenuList(menuView.getId(),menuList));
        }
        return menuViewList;
    }

    /**
     * Description:
     * <将返回给前台的菜单列表中的菜单id和大小为0的子菜单置空>
     * @author songcx
     * @date 9:26 2019/8/27
     * @param list 1
     **/
    private void setMenuViewIdNull(List<MenuView> list){
        for(MenuView menuView:list){
            menuView.setId(null);
            if(!menuView.getChildren().isEmpty()){
                setMenuViewIdNull(menuView.getChildren());
            }
            if(menuView.getChildren().isEmpty()){
                menuView.setChildren(null);
            }
        }
    }
}
